package com.stox.repository.file;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

import com.stox.io.ReverseLineInputStream;
import com.stox.util.PathUtil;

public abstract class AbstractFileRepository<T, V> {

	public static interface EntityIterator<T>{
		public boolean shouldContinue(T entity);
	}
	
	private Map<String, ReverseLineInputStream> inputStreams = new HashMap<String, ReverseLineInputStream>();
	private Map<String, BufferedWriter> writers = new HashMap<String, BufferedWriter>();
	
	protected abstract T deserialize(String text, V partitionModel);
	
	protected abstract String serialize(T entity);
	
	protected abstract String getFilePath(V partitionModel);
	
	public AbstractFileRepository() {
		Runtime.getRuntime().addShutdownHook(new Thread(){
			@Override
			public void run() {
				try{
					close();
				}catch(Exception e){
					e.printStackTrace();
				}
			}
		});
	}
	
	public String getFilebasePath(){
		return PathUtil.getRootPath()+File.separator+"filebase";
	}
	
	public void close() throws Exception{
		for(BufferedWriter writer : writers.values()){
			synchronized(writer){
				writer.flush();
				writer.close();
			}
		}
	}
	
	public void flush(){
		for(BufferedWriter writer : writers.values()){
			synchronized(writer){
				try {
					writer.flush();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	protected void iterate(EntityIterator<T> iterator, V partitionModel){
		try{
			String key = getFilePath(partitionModel);
			ReverseLineInputStream inputStream = inputStreams.get(key);
			if(null == inputStream) return;
			inputStream.reset();
			BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
			String line = null;
			while((line = r.readLine()) != null
					&& (null == iterator || 
						iterator.shouldContinue(deserialize(line, partitionModel)))){				
			}
		}catch(Exception e){
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	public T save(T entity, V partitionModel) {
		BufferedWriter writer = writers.get(getFilePath(partitionModel));
		if(null == writer) return null;
		synchronized(writer){
			try {
				writer.write(entity.toString()+System.getProperty("line.separator"));
				writer.flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return entity;
	}

	public void createPartition(V partitionModel) {
		try {
			String filePath = getFilePath(partitionModel);
			if(null == filePath) return;
			File file = new File(filePath);
			if(!file.exists()){
				file.getParentFile().mkdirs();
				file.createNewFile();
			}
			cache(filePath);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	protected void cache(String filePath) throws IOException{
		File file = new File(filePath);
		ReverseLineInputStream inputStream = new ReverseLineInputStream(file); 
		inputStreams.put(filePath, inputStream);
		writers.put(filePath, new BufferedWriter(new FileWriter(filePath,true)));
	}
	
}
